use std::{vec};

// use swc_ecma_utils::quote_expr;
// use crate::descriptor::field_descriptor_proto::Label;
use swc_common::DUMMY_SP;
use swc_ecma_ast::*;
use swc_ecma_ast::{MemberExpr, MemberProp, IfStmt, ReturnStmt, Lit,
    Accessibility, BinaryOp, Class, ClassMember, ClassMethod, Decl, ExportDecl, Expr,
    MethodKind, ModuleDecl, ModuleItem, Param, PropName, Stmt,
    TsEntityName, TsType, TsTypeAnn, TsTypeRef,
};
// use heck::{ToLowerCamelCase, ToUpperCamelCase};
use swc_ecma_utils::quote_ident;
use crate::{
    descriptor::{
        field_descriptor_proto::Type, DescriptorProto, FieldDescriptorProto,
    },
    context::{Context},
};

use super::{
    common::{normalize_name},
    LarkPbRuntime,
};

impl LarkPbRuntime {
    // Generate fromObject static method for a class
    pub(super) fn generate_from_object_method(
        &self,
        _ctx: &mut Context,
        descriptor: &DescriptorProto,
    ) -> ClassMember {
        let message_name = descriptor.name();
        let message_ident = quote_ident!(message_name);
        let interface_name = format!("I{}", message_name);
        let interface_ident = quote_ident!(interface_name);

        // Create simple method body: return MessageName.$meta.fromObject.fromObject(obj);
        let method_body = vec![Stmt::Return(ReturnStmt {
            span: DUMMY_SP,
            arg: Some(Box::new(Expr::Call(CallExpr {
                span: DUMMY_SP,
                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                    span: DUMMY_SP,
                    obj: Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(message_ident.clone())),
                            prop: MemberProp::Ident(quote_ident!("$meta")),
                        })),
                        prop: MemberProp::Ident(quote_ident!("fromObject")),
                    })),
                    prop: MemberProp::Ident(quote_ident!("fromObject")),
                }))),
                args: vec![ExprOrSpread {
                    spread: None,
                    expr: Box::new(Expr::Ident(quote_ident!("obj"))),
                }],
                type_args: None,
            }))),
        })];

        // Return fromObject method as ClassMember
        ClassMember::Method(ClassMethod {
            span: DUMMY_SP,
            key: PropName::Ident(quote_ident!("fromObject")),
            function: Box::new(Function {
                params: vec![Param {
                    span: DUMMY_SP,
                    decorators: vec![],
                    pat: Pat::Ident(BindingIdent {
                        id: quote_ident!("obj"),
                        type_ann: Some(Box::new(TsTypeAnn {
                            span: DUMMY_SP,
                            type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                span: DUMMY_SP,
                                type_name: TsEntityName::Ident(interface_ident),
                                type_params: None,
                            })),
                        })),
                    }),
                }],
                decorators: vec![],
                span: DUMMY_SP,
                body: Some(BlockStmt {
                    span: DUMMY_SP,
                    stmts: method_body,
                }),
                is_generator: false,
                is_async: false,
                type_params: None,
                return_type: Some(Box::new(TsTypeAnn {
                    span: DUMMY_SP,
                    type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                        span: DUMMY_SP,
                        type_name: TsEntityName::Ident(message_ident),
                        type_params: None,
                    })),
                })),
            }),
            kind: MethodKind::Method,
            is_static: true,
            accessibility: Some(Accessibility::Public),
            is_abstract: false,
            is_optional: false,
            is_override: false,
        })
    }

    // Generate toObject instance method for a class
    pub(super) fn generate_to_object_method(
        &self,
        _ctx: &mut Context,
        descriptor: &DescriptorProto,
    ) -> ClassMember {
        let message_name = descriptor.name();
        let message_ident = quote_ident!(message_name);

        // Create simple method body: return MessageName.$meta.toObject.toObject(this);
        let method_body = vec![Stmt::Return(ReturnStmt {
            span: DUMMY_SP,
            arg: Some(Box::new(Expr::Call(CallExpr {
                span: DUMMY_SP,
                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                    span: DUMMY_SP,
                    obj: Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(message_ident)),
                            prop: MemberProp::Ident(quote_ident!("$meta")),
                        })),
                        prop: MemberProp::Ident(quote_ident!("toObject")),
                    })),
                    prop: MemberProp::Ident(quote_ident!("toObject")),
                }))),
                args: vec![ExprOrSpread {
                    spread: None,
                    expr: Box::new(Expr::This(swc_ecma_ast::ThisExpr { span: DUMMY_SP })),
                }],
                type_args: None,
            }))),
        })];

        // Return toObject method as ClassMember
        ClassMember::Method(ClassMethod {
            span: DUMMY_SP,
            key: PropName::Ident(quote_ident!("toObject")),
            function: Box::new(Function {
                params: vec![],
                decorators: vec![],
                span: DUMMY_SP,
                body: Some(BlockStmt {
                    span: DUMMY_SP,
                    stmts: method_body,
                }),
                is_generator: false,
                is_async: false,
                type_params: None,
                return_type: Some(Box::new(TsTypeAnn {
                    span: DUMMY_SP,
                    type_ann: Box::new(TsType::TsKeywordType(TsKeywordType {
                        span: DUMMY_SP,
                        kind: TsKeywordTypeKind::TsStringKeyword,
                    })),
                })),
            }),
            kind: MethodKind::Method,
            is_static: false,
            accessibility: Some(Accessibility::Public),
            is_abstract: false,
            is_optional: false,
            is_override: false,
        })
    }

    // Helper method to generate field conversion expressions for fromObject
    fn generate_from_object_field_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("object"))),
            prop: MemberProp::Ident(quote_ident!(field.name())),
        });

        if field.is_repeated() {
            if field.is_map(ctx) {
                // Handle map fields
                self.generate_map_from_object_conversion(ctx, field, base64)
            } else {
                // Handle repeated fields
                self.generate_repeated_from_object_conversion(ctx, field, base64)
            }
        } else {
            // Handle single fields
            self.generate_single_from_object_conversion(ctx, field, base64)
        }
    }

    // Helper method to generate single field conversion for fromObject
    fn generate_single_from_object_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("object"))),
            prop: MemberProp::Ident(quote_ident!(field.name())),
        });

        match field.type_() {
            Type::TYPE_DOUBLE | Type::TYPE_FLOAT => {
                // Number(object.fieldName)
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Number")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => {
                // Safe integer conversion: typeof object.fieldName === "number" ? Math.floor(object.fieldName) : 0
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(field_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "number".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("Math"))),
                            prop: MemberProp::Ident(quote_ident!("floor")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(field_access),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_UINT32 | Type::TYPE_FIXED32 => {
                // object.fieldName >>> 0
                Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::ZeroFillRShift,
                    left: Box::new(field_access),
                    right: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                // BigInt(object.fieldName)
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("BigInt")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BOOL => {
                // Boolean(object.fieldName)
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Boolean")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_STRING => {
                // String(object.fieldName)
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("String")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BYTES => {
                // typeof object.fieldName === 'string' ? base64.decode(object.fieldName) : object.fieldName
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(field_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "string".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(base64.clone())),
                            prop: MemberProp::Ident(quote_ident!("decode")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(field_access.clone()),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(field_access),
                })
            },
            Type::TYPE_ENUM => {
                // Safe enum conversion with fallback to default value
                let enum_type_name = field.type_name().trim_start_matches('.');
                let enum_ident = quote_ident!(enum_type_name.split('.').last().unwrap_or(enum_type_name));

                // Generate: typeof object.fieldName === 'string' ?
                //   (EnumType[object.fieldName] !== undefined ? EnumType[object.fieldName] : 0) :
                //   object.fieldName
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(field_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "string".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Cond(CondExpr {
                        span: DUMMY_SP,
                        test: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::NotEqEq,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(enum_ident.clone())),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(field_access.clone()),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("undefined"))),
                        })),
                        cons: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(enum_ident)),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(field_access.clone()),
                            }),
                        })),
                        alt: Box::new(Expr::Lit(Lit::Num(Number {
                            span: DUMMY_SP,
                            value: 0.0,
                            raw: None,
                        }))),
                    })),
                    alt: Box::new(field_access),
                })
            },
            Type::TYPE_MESSAGE => {
                // NestedMessageType.fromObject(object.fieldName)
                let message_type_name = field.type_name().trim_start_matches('.');
                let message_ident = quote_ident!(message_type_name.split('.').last().unwrap_or(message_type_name));

                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(message_ident)),
                        prop: MemberProp::Ident(quote_ident!("fromObject")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            _ => field_access,
        }
    }

    // Helper method to generate repeated field conversion for fromObject
    fn generate_repeated_from_object_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("object"))),
            prop: MemberProp::Ident(quote_ident!(field.name())),
        });

        // object.fieldName.map(item => conversion_logic)
        let element_conversion = self.generate_element_conversion_for_repeated(ctx, field, base64);

        Expr::Call(CallExpr {
            span: DUMMY_SP,
            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                span: DUMMY_SP,
                obj: Box::new(field_access),
                prop: MemberProp::Ident(quote_ident!("map")),
            }))),
            args: vec![ExprOrSpread {
                spread: None,
                expr: Box::new(Expr::Arrow(ArrowExpr {
                    span: DUMMY_SP,
                    params: vec![Pat::Ident(BindingIdent {
                        id: quote_ident!("item"),
                        type_ann: None,
                    })],
                    body: Box::new(BlockStmtOrExpr::Expr(Box::new(element_conversion))),
                    is_async: false,
                    is_generator: false,
                    type_params: None,
                    return_type: None,
                })),
            }],
            type_args: None,
        })
    }

    // Helper method to generate element conversion for repeated fields
    fn generate_element_conversion_for_repeated(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let item_access = Expr::Ident(quote_ident!("item"));

        match field.type_() {
            Type::TYPE_DOUBLE | Type::TYPE_FLOAT => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Number")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => {
                // Safe integer conversion for repeated fields
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(item_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "number".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("Math"))),
                            prop: MemberProp::Ident(quote_ident!("floor")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(item_access),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_UINT32 | Type::TYPE_FIXED32 => {
                Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::ZeroFillRShift,
                    left: Box::new(item_access),
                    right: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("BigInt")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BOOL => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Boolean")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_STRING => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("String")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BYTES => {
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(item_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "string".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(base64.clone())),
                            prop: MemberProp::Ident(quote_ident!("decode")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(item_access.clone()),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(item_access),
                })
            },
            Type::TYPE_ENUM => {
                let enum_type_name = field.type_name().trim_start_matches('.');
                let enum_ident = quote_ident!(enum_type_name.split('.').last().unwrap_or(enum_type_name));

                // Safe enum conversion for repeated fields
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(item_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "string".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Cond(CondExpr {
                        span: DUMMY_SP,
                        test: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::NotEqEq,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(enum_ident.clone())),
                                prop: MemberProp::Computed(ComputedPropName {
                                    span: DUMMY_SP,
                                    expr: Box::new(item_access.clone()),
                                }),
                            })),
                            right: Box::new(Expr::Ident(quote_ident!("undefined"))),
                        })),
                        cons: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(enum_ident)),
                            prop: MemberProp::Computed(ComputedPropName {
                                span: DUMMY_SP,
                                expr: Box::new(item_access.clone()),
                            }),
                        })),
                        alt: Box::new(Expr::Lit(Lit::Num(Number {
                            span: DUMMY_SP,
                            value: 0.0,
                            raw: None,
                        }))),
                    })),
                    alt: Box::new(item_access),
                })
            },
            Type::TYPE_MESSAGE => {
                let message_type_name = field.type_name().trim_start_matches('.');
                let message_ident = quote_ident!(message_type_name.split('.').last().unwrap_or(message_type_name));

                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(message_ident)),
                        prop: MemberProp::Ident(quote_ident!("fromObject")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            _ => item_access,
        }
    }

    // Helper method to generate map field conversion for fromObject
    fn generate_map_from_object_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("object"))),
            prop: MemberProp::Ident(quote_ident!(field.name())),
        });

        // Get map entry type information
        if let Some(map_desc) = ctx.get_map_type(field.type_name()) {
            let value_field = &map_desc.field[1]; // Value field is always the second field

            // Create conversion logic for map values
            // const result = {}; for (const key of Object.keys(object.fieldName)) { result[key] = convertValue(object.fieldName[key]); } return result;

            // For now, return a simplified version that handles basic map conversion
            // TODO: Implement full map conversion with proper value type handling
            Expr::Call(CallExpr {
                span: DUMMY_SP,
                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                    span: DUMMY_SP,
                    obj: Box::new(Expr::Ident(quote_ident!("Object"))),
                    prop: MemberProp::Ident(quote_ident!("fromEntries")),
                }))),
                args: vec![ExprOrSpread {
                    spread: None,
                    expr: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Call(CallExpr {
                                span: DUMMY_SP,
                                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Ident(quote_ident!("Object"))),
                                    prop: MemberProp::Ident(quote_ident!("entries")),
                                }))),
                                args: vec![ExprOrSpread {
                                    spread: None,
                                    expr: Box::new(field_access),
                                }],
                                type_args: None,
                            })),
                            prop: MemberProp::Ident(quote_ident!("map")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(Expr::Arrow(ArrowExpr {
                                span: DUMMY_SP,
                                params: vec![Pat::Array(ArrayPat {
                                    span: DUMMY_SP,
                                    elems: vec![
                                        Some(Pat::Ident(BindingIdent {
                                            id: quote_ident!("k"),
                                            type_ann: None,
                                        })),
                                        Some(Pat::Ident(BindingIdent {
                                            id: quote_ident!("v"),
                                            type_ann: None,
                                        })),
                                    ],
                                    optional: false,
                                    type_ann: None,
                                })],
                                body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Array(ArrayLit {
                                    span: DUMMY_SP,
                                    elems: vec![
                                        Some(ExprOrSpread {
                                            spread: None,
                                            expr: Box::new(Expr::Ident(quote_ident!("k"))),
                                        }),
                                        Some(ExprOrSpread {
                                            spread: None,
                                            expr: Box::new(self.generate_map_value_conversion(value_field, base64)),
                                        }),
                                    ],
                                })))),
                                is_async: false,
                                is_generator: false,
                                type_params: None,
                                return_type: None,
                            })),
                        }],
                        type_args: None,
                    })),
                }],
                type_args: None,
            })
        } else {
            // Fallback: return the field access directly
            field_access
        }
    }

    // Helper method to generate map value conversion
    fn generate_map_value_conversion(
        &self,
        value_field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let value_access = Expr::Ident(quote_ident!("v"));

        match value_field.type_() {
            Type::TYPE_DOUBLE | Type::TYPE_FLOAT => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Number")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(value_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_INT32 | Type::TYPE_SINT32 | Type::TYPE_SFIXED32 => {
                // Safe integer conversion: typeof v === "number" ? Math.floor(v) : 0
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(value_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "number".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("Math"))),
                            prop: MemberProp::Ident(quote_ident!("floor")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(value_access),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_UINT32 | Type::TYPE_FIXED32 => {
                Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::ZeroFillRShift,
                    left: Box::new(value_access),
                    right: Box::new(Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))),
                })
            },
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("BigInt")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(value_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BOOL => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("Boolean")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(value_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_STRING => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("String")))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(value_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_BYTES => {
                Expr::Cond(CondExpr {
                    span: DUMMY_SP,
                    test: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::EqEqEq,
                        left: Box::new(Expr::Unary(UnaryExpr {
                            span: DUMMY_SP,
                            op: UnaryOp::TypeOf,
                            arg: Box::new(value_access.clone()),
                        })),
                        right: Box::new(Expr::Lit(Lit::Str(Str {
                            span: DUMMY_SP,
                            value: "string".into(),
                            raw: None,
                        }))),
                    })),
                    cons: Box::new(Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(base64.clone())),
                            prop: MemberProp::Ident(quote_ident!("decode")),
                        }))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(value_access.clone()),
                        }],
                        type_args: None,
                    })),
                    alt: Box::new(value_access),
                })
            },
            Type::TYPE_MESSAGE => {
                let message_type_name = value_field.type_name().trim_start_matches('.');
                let message_ident = quote_ident!(message_type_name.split('.').last().unwrap_or(message_type_name));

                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(message_ident)),
                        prop: MemberProp::Ident(quote_ident!("fromObject")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(value_access),
                    }],
                    type_args: None,
                })
            },
            _ => value_access,
        }
    }

    // Generate toObject method
    // pub(super) fn print_to_object(
    //     &self,
    //     ctx: &mut Context,
    //     descriptor: &DescriptorProto,
    //     ns: &[String],
    // ) -> ModuleItem {
    //     // Note: Removed base64 import as per requirement 1.1, 1.2, 1.3
    //     // Temporary placeholder to avoid compilation errors - will be removed in later tasks
    //     let base64 = quote_ident!("base64_placeholder");

    //     let message_name = descriptor.name();
    //     let message_ident = quote_ident!(message_name);

    //     // Create method body statements
    //     let mut stmts = Vec::new();

    //     // const obj: { [k: string]: any } = {};
    //     stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
    //         span: DUMMY_SP,
    //         kind: VarDeclKind::Const,
    //         decls: vec![VarDeclarator {
    //             span: DUMMY_SP,
    //             name: Pat::Ident(BindingIdent {
    //                 id: quote_ident!("obj"),
    //                 type_ann: Some(Box::new(TsTypeAnn {
    //                     span: DUMMY_SP,
    //                     type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
    //                         span: DUMMY_SP,
    //                         type_name: TsEntityName::Ident(quote_ident!("Record")),
    //                         type_params: Some(Box::new(TsTypeParamInstantiation {
    //                             span: DUMMY_SP,
    //                             params: vec![
    //                                 Box::new(TsType::TsKeywordType(TsKeywordType {
    //                                     span: DUMMY_SP,
    //                                     kind: TsKeywordTypeKind::TsStringKeyword,
    //                                 })),
    //                                 Box::new(TsType::TsKeywordType(TsKeywordType {
    //                                     span: DUMMY_SP,
    //                                     kind: TsKeywordTypeKind::TsAnyKeyword,
    //                                 })),
    //                             ],
    //                         })),
    //                     })),
    //                 })),
    //             }),
    //             init: Some(Box::new(Expr::Object(ObjectLit {
    //                 span: DUMMY_SP,
    //                 props: vec![],
    //             }))),
    //             definite: false,
    //         }],
    //         declare: false,
    //     }))));

    //     // Generate field conversion logic for toObject
    //     for field in &descriptor.field {
    //         if !field.has_oneof_index() {
    //             let field_name = field.name();
    //             let _field_ident = quote_ident!(normalize_name(field_name));

    //             // Generate conversion expression based on field type
    //             let conversion_expr = self.generate_to_object_field_conversion(ctx, field, &base64);

    //             // Generate condition based on field type for omitting default values
    //             let condition_expr = self.generate_to_object_condition(ctx, field);

    //             // if (condition) { obj.fieldName = conversion; }
    //             stmts.push(Stmt::If(IfStmt {
    //                 span: DUMMY_SP,
    //                 test: Box::new(condition_expr),
    //                 cons: Box::new(Stmt::Block(BlockStmt {
    //                     span: DUMMY_SP,
    //                     stmts: vec![Stmt::Expr(ExprStmt {
    //                         span: DUMMY_SP,
    //                         expr: Box::new(Expr::Assign(AssignExpr {
    //                             span: DUMMY_SP,
    //                             op: AssignOp::Assign,
    //                             left: AssignTarget::Simple(SimpleAssignTarget::Member(MemberExpr {
    //                                 span: DUMMY_SP,
    //                                 obj: Box::new(Expr::Ident(quote_ident!("obj"))),
    //                                 prop: MemberProp::Ident(quote_ident!(field_name)),
    //                             })),
    //                             right: Box::new(conversion_expr),
    //                         })),
    //                     })],
    //                 })),
    //                 alt: None,
    //             }));
    //         }
    //     }

    //     // return obj;
    //     stmts.push(Stmt::Return(ReturnStmt {
    //         span: DUMMY_SP,
    //         arg: Some(Box::new(Expr::Ident(quote_ident!("obj")))),
    //     }));

    //     // Create toObject method
    //     let to_object_method = ClassMethod {
    //         span: DUMMY_SP,
    //         key: PropName::Ident(quote_ident!("toObject")),
    //         function: Box::new(Function {
    //             params: vec![],
    //             decorators: vec![],
    //             span: DUMMY_SP,
    //             body: Some(BlockStmt {
    //                 span: DUMMY_SP,
    //                 stmts,
    //             }),
    //             is_generator: false,
    //             is_async: false,
    //             type_params: None,
    //             return_type: Some(Box::new(TsTypeAnn {
    //                 span: DUMMY_SP,
    //                 type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
    //                     span: DUMMY_SP,
    //                     type_name: TsEntityName::Ident(quote_ident!("Record")),
    //                     type_params: Some(Box::new(TsTypeParamInstantiation {
    //                         span: DUMMY_SP,
    //                         params: vec![
    //                             Box::new(TsType::TsKeywordType(TsKeywordType {
    //                                 span: DUMMY_SP,
    //                                 kind: TsKeywordTypeKind::TsStringKeyword,
    //                             })),
    //                             Box::new(TsType::TsKeywordType(TsKeywordType {
    //                                 span: DUMMY_SP,
    //                                 kind: TsKeywordTypeKind::TsAnyKeyword,
    //                             })),
    //                         ],
    //                     })),
    //                 })),
    //             })),
    //         }),
    //         kind: MethodKind::Method,
    //         is_static: false,
    //         accessibility: Some(Accessibility::Public),
    //         is_abstract: false,
    //         is_optional: false,
    //         is_override: false,
    //     };

    //     // Create class definition
    //     let class = Class {
    //         span: DUMMY_SP,
    //         decorators: vec![],
    //         body: vec![ClassMember::Method(to_object_method)],
    //         super_class: None,
    //         is_abstract: false,
    //         type_params: None,
    //         super_type_params: None,
    //         implements: vec![],
    //     };

    //     ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
    //         span: DUMMY_SP,
    //         decl: Decl::Class(ClassDecl {
    //             ident: quote_ident!(format!("{}ToObject", message_name)),
    //             declare: false,
    //             class: Box::new(class),
    //         }),
    //     }))
    // }

    // Helper method to generate field conversion expressions for toObject
    fn generate_to_object_field_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("this"))),
            prop: MemberProp::Ident(quote_ident!(normalize_name(field.name()))),
        });

        if field.is_repeated() {
            if field.is_map(ctx) {
                // Handle map fields - TODO: implement proper map conversion
                field_access
            } else {
                // Handle repeated fields: this.fieldName.map(item => conversion_logic)
                let element_conversion = self.generate_element_conversion_for_to_object(ctx, field, base64);

                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(field_access),
                        prop: MemberProp::Ident(quote_ident!("map")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(Expr::Arrow(ArrowExpr {
                            span: DUMMY_SP,
                            params: vec![Pat::Ident(BindingIdent {
                                id: quote_ident!("item"),
                                type_ann: None,
                            })],
                            body: Box::new(BlockStmtOrExpr::Expr(Box::new(element_conversion))),
                            is_async: false,
                            is_generator: false,
                            type_params: None,
                            return_type: None,
                        })),
                    }],
                    type_args: None,
                })
            }
        } else {
            // Handle single fields
            self.generate_single_to_object_conversion(ctx, field, base64)
        }
    }

    // Helper method to generate single field conversion for toObject
    fn generate_single_to_object_conversion(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("this"))),
            prop: MemberProp::Ident(quote_ident!(normalize_name(field.name()))),
        });

        match field.type_() {
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                // this.fieldName.toString()
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(field_access),
                        prop: MemberProp::Ident(quote_ident!("toString")),
                    }))),
                    args: vec![],
                    type_args: None,
                })
            },
            Type::TYPE_BYTES => {
                // base64.encode(this.fieldName)
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(base64.clone())),
                        prop: MemberProp::Ident(quote_ident!("encode")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(field_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_MESSAGE => {
                // this.fieldName.toObject()
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(field_access),
                        prop: MemberProp::Ident(quote_ident!("toObject")),
                    }))),
                    args: vec![],
                    type_args: None,
                })
            },
            _ => {
                // For other types, return the field value directly
                field_access
            }
        }
    }

    // Helper method to generate element conversion for repeated fields in toObject
    fn generate_element_conversion_for_to_object(
        &self,
        ctx: &mut Context,
        field: &FieldDescriptorProto,
        base64: &Ident,
    ) -> Expr {
        let item_access = Expr::Ident(quote_ident!("item"));

        match field.type_() {
            Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(item_access),
                        prop: MemberProp::Ident(quote_ident!("toString")),
                    }))),
                    args: vec![],
                    type_args: None,
                })
            },
            Type::TYPE_BYTES => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(Expr::Ident(base64.clone())),
                        prop: MemberProp::Ident(quote_ident!("encode")),
                    }))),
                    args: vec![ExprOrSpread {
                        spread: None,
                        expr: Box::new(item_access),
                    }],
                    type_args: None,
                })
            },
            Type::TYPE_MESSAGE => {
                Expr::Call(CallExpr {
                    span: DUMMY_SP,
                    callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                        span: DUMMY_SP,
                        obj: Box::new(item_access),
                        prop: MemberProp::Ident(quote_ident!("toObject")),
                    }))),
                    args: vec![],
                    type_args: None,
                })
            },
            _ => item_access,
        }
    }

    // Helper method to generate conditions for omitting default values in toObject
    fn generate_to_object_condition(&self, ctx: &mut Context, field: &FieldDescriptorProto) -> Expr {
        let field_access = Expr::Member(MemberExpr {
            span: DUMMY_SP,
            obj: Box::new(Expr::Ident(quote_ident!("this"))),
            prop: MemberProp::Ident(quote_ident!(normalize_name(field.name()))),
        });

        if field.is_repeated() {
            if field.is_map(ctx) {
                // For map fields: this.fieldName != null && Object.keys(this.fieldName).length > 0
                Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::LogicalAnd,
                    left: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEq,
                        left: Box::new(field_access.clone()),
                        right: Box::new(Expr::Ident(quote_ident!("null"))),
                    })),
                    right: Box::new(Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::Gt,
                        left: Box::new(Expr::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Call(CallExpr {
                                span: DUMMY_SP,
                                callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                    span: DUMMY_SP,
                                    obj: Box::new(Expr::Ident(quote_ident!("Object"))),
                                    prop: MemberProp::Ident(quote_ident!("keys")),
                                }))),
                                args: vec![ExprOrSpread {
                                    spread: None,
                                    expr: Box::new(field_access),
                                }],
                                type_args: None,
                            })),
                            prop: MemberProp::Ident(quote_ident!("length")),
                        })),
                        right: Box::new(Expr::Lit(Lit::Num(Number {
                            span: DUMMY_SP,
                            value: 0.0,
                            raw: None,
                        }))),
                    })),
                })
            } else {
                // For repeated fields: this.fieldName?.length
                Expr::Member(MemberExpr {
                    span: DUMMY_SP,
                    obj: Box::new(Expr::OptChain(OptChainExpr {
                        span: DUMMY_SP,
                        base: Box::new(OptChainBase::Member(MemberExpr {
                            span: DUMMY_SP,
                            obj: Box::new(Expr::Ident(quote_ident!("this"))),
                            prop: MemberProp::Ident(quote_ident!(normalize_name(field.name()))),
                        })),
                        optional: true,
                    })),
                    prop: MemberProp::Ident(quote_ident!("length")),
                })
            }
        } else {
            // For single fields, check against default values
            let default_value = match field.type_() {
                Type::TYPE_DOUBLE | Type::TYPE_FLOAT | Type::TYPE_INT32 | Type::TYPE_UINT32 |
                Type::TYPE_SINT32 | Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 => {
                    Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))
                },
                Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_SINT64 | Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 => {
                    Expr::Call(CallExpr {
                        span: DUMMY_SP,
                        callee: Callee::Expr(Box::new(Expr::Ident(quote_ident!("BigInt")))),
                        args: vec![ExprOrSpread {
                            spread: None,
                            expr: Box::new(Expr::Lit(Lit::Num(Number {
                                span: DUMMY_SP,
                                value: 0.0,
                                raw: None,
                            }))),
                        }],
                        type_args: None,
                    })
                },
                Type::TYPE_BOOL => {
                    Expr::Lit(Lit::Bool(Bool {
                        span: DUMMY_SP,
                        value: false,
                    }))
                },
                Type::TYPE_STRING => {
                    Expr::Lit(Lit::Str(Str {
                        span: DUMMY_SP,
                        value: "".into(),
                        raw: None,
                    }))
                },
                Type::TYPE_BYTES => {
                    // this.fieldName != null && this.fieldName.length > 0
                    return Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::LogicalAnd,
                        left: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::NotEq,
                            left: Box::new(field_access.clone()),
                            right: Box::new(Expr::Ident(quote_ident!("null"))),
                        })),
                        right: Box::new(Expr::Bin(BinExpr {
                            span: DUMMY_SP,
                            op: BinaryOp::Gt,
                            left: Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(field_access),
                                prop: MemberProp::Ident(quote_ident!("length")),
                            })),
                            right: Box::new(Expr::Lit(Lit::Num(Number {
                                span: DUMMY_SP,
                                value: 0.0,
                                raw: None,
                            }))),
                        })),
                    });
                },
                Type::TYPE_ENUM => {
                    Expr::Lit(Lit::Num(Number {
                        span: DUMMY_SP,
                        value: 0.0,
                        raw: None,
                    }))
                },
                Type::TYPE_MESSAGE => {
                    // For message types: this.fieldName != null
                    return Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEq,
                        left: Box::new(field_access),
                        right: Box::new(Expr::Ident(quote_ident!("null"))),
                    });
                },
                _ => {
                    return Expr::Bin(BinExpr {
                        span: DUMMY_SP,
                        op: BinaryOp::NotEq,
                        left: Box::new(field_access),
                        right: Box::new(Expr::Ident(quote_ident!("null"))),
                    });
                }
            };

            // this.fieldName != null && this.fieldName !== defaultValue
            Expr::Bin(BinExpr {
                span: DUMMY_SP,
                op: BinaryOp::LogicalAnd,
                left: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEq,
                    left: Box::new(field_access.clone()),
                    right: Box::new(Expr::Ident(quote_ident!("null"))),
                })),
                right: Box::new(Expr::Bin(BinExpr {
                    span: DUMMY_SP,
                    op: BinaryOp::NotEqEq,
                    left: Box::new(field_access),
                    right: Box::new(default_value),
                })),
            })
        }
    }

    // Generate $FromObject class for codec file
    pub(super) fn print_from_object(
        &self,
        ctx: &mut Context,
        descriptor: &DescriptorProto,
        ns: &[String],
    ) -> ModuleItem {
        let message_name = descriptor.name();
        let interface_name = format!("I{}", message_name);
        let interface_ident = quote_ident!(interface_name);
        let message_ident = quote_ident!(message_name);

        // Import FromObject interface
        let from_object_interface = self.get_lark_import(ctx, Self::LARK_FROM_OBJECT_NAME);

        // Create the fromObject method implementation
        let from_object_method = ClassMethod {
            span: DUMMY_SP,
            key: PropName::Ident(quote_ident!("fromObject")),
            function: Box::new(Function {
                params: vec![Param {
                    span: DUMMY_SP,
                    decorators: vec![],
                    pat: Pat::Ident(BindingIdent {
                        id: quote_ident!("obj"),
                        type_ann: Some(Box::new(TsTypeAnn {
                            span: DUMMY_SP,
                            type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                span: DUMMY_SP,
                                type_name: TsEntityName::Ident(interface_ident.clone()),
                                type_params: None,
                            })),
                        })),
                    }),
                }],
                decorators: vec![],
                span: DUMMY_SP,
                body: Some(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![Stmt::Return(ReturnStmt {
                        span: DUMMY_SP,
                        arg: Some(Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(message_ident.clone())),
                                prop: MemberProp::Ident(quote_ident!("create")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: Box::new(Expr::Ident(quote_ident!("obj"))),
                            }],
                            type_args: None,
                        }))),
                    })],
                }),
                is_generator: false,
                is_async: false,
                type_params: None,
                return_type: Some(Box::new(TsTypeAnn {
                    span: DUMMY_SP,
                    type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                        span: DUMMY_SP,
                        type_name: TsEntityName::Ident(message_ident.clone()),
                        type_params: None,
                    })),
                })),
            }),
            kind: MethodKind::Method,
            is_static: false,
            accessibility: Some(Accessibility::Public),
            is_abstract: false,
            is_optional: false,
            is_override: false,
        };

        // Create the implements clause
        let implement = TsExprWithTypeArgs {
            span: DUMMY_SP,
            expr: Box::new(Expr::Ident(from_object_interface)),
            type_args: Some(Box::new(TsTypeParamInstantiation {
                span: DUMMY_SP,
                params: vec![
                    Box::new(TsType::TsTypeRef(TsTypeRef {
                        span: DUMMY_SP,
                        type_name: TsEntityName::Ident(interface_ident),
                        type_params: None,
                    })),
                    Box::new(TsType::TsTypeRef(TsTypeRef {
                        span: DUMMY_SP,
                        type_name: TsEntityName::Ident(message_ident),
                        type_params: None,
                    })),
                ],
            })),
        };

        // Create the class
        let class = Class {
            span: DUMMY_SP,
            decorators: vec![],
            body: vec![ClassMember::Method(from_object_method)],
            super_class: None,
            is_abstract: false,
            type_params: None,
            super_type_params: None,
            implements: vec![implement],
        };

        // Export the class
        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
            span: DUMMY_SP,
            decl: Decl::Class(ClassDecl {
                ident: quote_ident!("$FromObject"),
                declare: false,
                class: Box::new(class),
            }),
        }))
    }

    // Generate $ToObject class for codec file
    pub(super) fn print_to_object(
        &self,
        ctx: &mut Context,
        descriptor: &DescriptorProto,
        _ns: &[String],
    ) -> ModuleItem {
        let message_name = descriptor.name();
        let message_ident = quote_ident!(message_name);

        // Import ToObject interface
        let to_object_interface = self.get_lark_import(ctx, Self::LARK_TO_OBJECT_NAME);

        // Create the toObject method implementation
        let to_object_method = ClassMethod {
            span: DUMMY_SP,
            key: PropName::Ident(quote_ident!("toObject")),
            function: Box::new(Function {
                params: vec![Param {
                    span: DUMMY_SP,
                    decorators: vec![],
                    pat: Pat::Ident(BindingIdent {
                        id: quote_ident!("msg"),
                        type_ann: Some(Box::new(TsTypeAnn {
                            span: DUMMY_SP,
                            type_ann: Box::new(TsType::TsTypeRef(TsTypeRef {
                                span: DUMMY_SP,
                                type_name: TsEntityName::Ident(message_ident.clone()),
                                type_params: None,
                            })),
                        })),
                    }),
                }],
                decorators: vec![],
                span: DUMMY_SP,
                body: Some(BlockStmt {
                    span: DUMMY_SP,
                    stmts: vec![Stmt::Return(ReturnStmt {
                        span: DUMMY_SP,
                        arg: Some(Box::new(Expr::Call(CallExpr {
                            span: DUMMY_SP,
                            callee: Callee::Expr(Box::new(Expr::Member(MemberExpr {
                                span: DUMMY_SP,
                                obj: Box::new(Expr::Ident(quote_ident!("JSON"))),
                                prop: MemberProp::Ident(quote_ident!("stringify")),
                            }))),
                            args: vec![ExprOrSpread {
                                spread: None,
                                expr: Box::new(Expr::Ident(quote_ident!("msg"))),
                            }],
                            type_args: None,
                        }))),
                    })],
                }),
                is_generator: false,
                is_async: false,
                type_params: None,
                return_type: Some(Box::new(TsTypeAnn {
                    span: DUMMY_SP,
                    type_ann: Box::new(TsType::TsKeywordType(TsKeywordType {
                        span: DUMMY_SP,
                        kind: TsKeywordTypeKind::TsStringKeyword,
                    })),
                })),
            }),
            kind: MethodKind::Method,
            is_static: false,
            accessibility: Some(Accessibility::Public),
            is_abstract: false,
            is_optional: false,
            is_override: false,
        };

        // Create the implements clause
        let implement = TsExprWithTypeArgs {
            span: DUMMY_SP,
            expr: Box::new(Expr::Ident(to_object_interface)),
            type_args: Some(Box::new(TsTypeParamInstantiation {
                span: DUMMY_SP,
                params: vec![
                    Box::new(TsType::TsTypeRef(TsTypeRef {
                        span: DUMMY_SP,
                        type_name: TsEntityName::Ident(message_ident),
                        type_params: None,
                    })),
                ],
            })),
        };

        // Create the class
        let class = Class {
            span: DUMMY_SP,
            decorators: vec![],
            body: vec![ClassMember::Method(to_object_method)],
            super_class: None,
            is_abstract: false,
            type_params: None,
            super_type_params: None,
            implements: vec![implement],
        };

        // Export the class
        ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
            span: DUMMY_SP,
            decl: Decl::Class(ClassDecl {
                ident: quote_ident!("$ToObject"),
                declare: false,
                class: Box::new(class),
            }),
        }))
    }
}
